package com.breeze.components.message.secure;

import cn.hutool.core.convert.BasicType;
import com.alibaba.fastjson2.JSON;
import com.breeze.components.message.secure.record.SecureInvokeRecord;
import jakarta.annotation.Resource;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * @author breeze
 */
@Slf4j
@Aspect
public class SecureInvokeAspect {

  @Resource private SecureInvokeService secureInvokeService;

  @Around("@annotation(secureInvoke)")
  public Object around(ProceedingJoinPoint joinPoint, SecureInvoke secureInvoke) throws Throwable {
    boolean async = secureInvoke.async();
    boolean inTransaction = TransactionSynchronizationManager.isActualTransactionActive();
    if (SecureInvokeHolder.isInvoking() || !inTransaction) {
      return joinPoint.proceed();
    }
    Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
    List<String> parameters =
        Stream.of(method.getParameterTypes())
            .map(type -> BasicType.wrap(type).getName())
            .collect(Collectors.toList());
    SecureInvokeDTO dto =
        SecureInvokeDTO.builder()
            .args(JSON.toJSONString(joinPoint.getArgs()))
            .className(method.getDeclaringClass().getName())
            .methodName(method.getName())
            .parameterTypes(JSON.toJSONString(parameters))
            .build();
    SecureInvokeRecord record =
        SecureInvokeRecord.builder()
            .secureInvokeDTO(dto)
            .maxRetryTimes(secureInvoke.maxRetryTimes())
            .nextRetryTime(
                LocalDateTime.now().plusSeconds(SecureInvokeService.RETRY_INTERVAL_SECOND))
            .build();
    secureInvokeService.invoke(record, async);
    return null;
  }
}
